import Konva from 'konva'
import { Vector2d } from 'konva/lib/types'
import { useLayoutEffect } from 'react'
import { Context, State } from './context'
import { bitmasks47 } from './data'
import { IndexGroup } from './index-group'

type Prop = Pick<Context, 'id' | 'gridsize' | 'eventbus'>

export default function Tiles(props: Prop) {
  useLayoutEffect(() => {
    console.log('useLayoutEffect')
    return handleLayoutEffect(props)
  }, [])

  return <div {...props} />
}

function handleLayoutEffect(props: Prop) {
  let ctx = buildContext()

  enableShowGrid()
  enableMouseScale()
  enableMoveOrSelect()
  enableParseImage()
  enableHilightGrid()
  enableRightClickMenu()

  return () => {
    ctx.stage.destroy()
  }

  function buildContext(): Context {
    const { id } = props
    const box = document.getElementById(id)!
    const stage = new Konva.Stage({
      container: id,
      width: box.clientWidth,
      height: box.clientHeight
    })

    const grid = new Konva.Group()
    const tiles = new IndexGroup({ globalCompositeOperation: 'source-over', gridSize: props.gridsize })
    const ui = new Konva.Group({ globalCompositeOperation: 'source-over' })

    stage.add(new Konva.Layer().add(tiles).add(grid).add(ui))

    return {
      stage,
      cursor: { x: 0, y: 0 },
      scale: 1,
      groups: {
        grid,
        tiles,
        ui,
      },
      ...props
    }
  }

  function updateState(state: Partial<State>) {
    ctx = {
      ...ctx,
      ...state
    }
    if (state.scale) {
      ctx.stage.scale({ x: state.scale, y: state.scale })
    }
    ctx.eventbus.emit('tilesStatusChanged', ctx)
  }


  // 绘制网格的函数
  function enableShowGrid() {
    const gridSize = ctx.gridsize
    const stage = ctx.stage
    const group = ctx.groups.grid

    group.destroyChildren()

    // 获取舞台的缩放比例和偏移量
    const scale = stage.scaleX()
    const scaledSize = gridSize * scale
    const offsetX = Math.floor(stage.x() / scaledSize) * gridSize
    const offsetY = Math.floor(stage.y() / scaledSize) * gridSize

    // 获取舞台的宽度和高度
    const scaleFactor = Math.min(scaledSize, gridSize / 4)
    const w = Math.floor(stage.width() / scaleFactor) * gridSize
    const h = Math.floor(stage.height() / scaleFactor) * gridSize

    const left = -2 * w - offsetX
    const right = 2 * w - offsetX
    const top = -2 * h - offsetY
    const bottom = 2 * h - offsetY

    // 绘制垂直网格线
    for (let x = left; x < right; x += gridSize) {
      const line = new Konva.Line({
        points: [x, top, x, bottom],
        stroke: 'rgba(255,255,255,0.25)',
        strokeWidth: 1,
      })
      group.add(line)
    }

    // 绘制水平网格线
    for (let y = top; y < bottom; y += gridSize) {
      const line = new Konva.Line({
        points: [left, y, right, y],
        stroke: 'rgba(255,255,255,0.25)',
        strokeWidth: 1,
      })
      group.add(line)
    }
  }

  function enableMouseScale() {
    const stage = ctx.stage
    const scaleBy = 1.1

    stage.on('wheel', (e) => {
      e.evt.preventDefault()

      const oldScale = stage.scaleX()
      const pointer = stage.getPointerPosition()
      if (!pointer) return

      const mousePointTo = {
        x: (pointer.x - stage.x()) / oldScale,
        y: (pointer.y - stage.y()) / oldScale
      }

      let newScale = e.evt.deltaY > 0 ? oldScale / scaleBy : oldScale * scaleBy

      if (newScale < 0.25) newScale = 0.25

      const newPos = {
        x: pointer.x - mousePointTo.x * newScale,
        y: pointer.y - mousePointTo.y * newScale
      }
      stage.position(newPos)

      updateState({ scale: newScale })
    })
  }

  function enableHilightGrid() {
    const stage = ctx.stage
    const gridSize = ctx.gridsize
    const group = ctx.groups.ui

    // 用于存储高亮网格的矩形
    let highlight: Konva.Rect | null = null

    stage.on('mousemove', () => {
      const cursor = getPointerGridIndex()
      updateState({ cursor })

      if (highlight) highlight.destroy()
      if (ctx.select?.selecting) return

      const { x, y } = indexToPosition(cursor)

      highlight = new Konva.Rect({
        x,
        y,
        width: gridSize,
        height: gridSize,
        stroke: 'white',
        strokeWidth: 2,
        dash: [5, 5],
        listening: false
      })
      group.add(highlight)
    })
  }

  function enableRightClickMenu() {
    const stage = ctx.stage
    stage.on('contextmenu', (e) => {
      e.evt.preventDefault()
      const menus = new Map<string, () => void>([
        getDefaultMenus(),
      ])

      addImageMenus(menus)
      addExportMenu(menus)

      ctx.eventbus.emit('tilesRightClick', {
        clientX: e.evt.clientX,
        clientY: e.evt.clientY,
        items: menus
      })
    })
  }

  function getDefaultMenus(): [string, () => void] {
    return ['reset zoom', () => {
      updateState({ scale: 1 })
    }]
  }

  function addImageMenus(menus: Map<string, () => void>) {
    addDeleteMenu(menus)
    addGenerateTilesMenu(menus)
  }

  function addGenerateTilesMenu(menus: Map<string, () => void>) {
    const { x, y } = getPointerGridIndex()

    menus.set('generate tiles', () => {
      const tiles = ctx.groups.tiles
      const node = tiles.getAt(x, y)
      if (!node)
        return

      node.cache()

      const cellSize = ctx.gridsize / 3

      for (const [dx, gridRow] of bitmasks47) {
        for (const [dy, bitmasks] of gridRow) {
          if (dx === 0 && dy === 0) continue
          const tile = node.clone() as Konva.Group

          const mask = new Konva.Group({
            type: 'mask',
            globalCompositeOperation: 'destination-out'
          })

          for (let i = 0; i < bitmasks.length; i++) {
            const row = bitmasks[i]
            for (let j = 0; j < row.length; j++) {
              const isBit = (x: number, y: number, bit: number) => {
                if (x < 0 || x >= 3 || y < 0 || y >= 3) return false
                return bitmasks[y]?.[x] === bit
              }

              if (isBit(j, i, 1)) continue

              // 根据相邻的 bitmask 值设置圆角
              const cornerRadius = [0, 0, 0, 0]

              if (isBit(j - 1, i, 1) && isBit(j, i - 1, 1)) cornerRadius[0] = cellSize
              if (isBit(j + 1, i, 1) && isBit(j, i - 1, 1)) cornerRadius[1] = cellSize
              if (isBit(j + 1, i, 1) && isBit(j, i + 1, 1)) cornerRadius[2] = cellSize
              if (isBit(j - 1, i, 1) && isBit(j, i + 1, 1)) cornerRadius[3] = cellSize

              mask.add(new Konva.Rect({
                x: j * cellSize,
                y: i * cellSize,
                width: cellSize + 1,
                height: cellSize + 1,
                fill: 'white',
                cornerRadius
              }))
            }
          }

          tile.add(mask)
          tiles.addAt(x + dx, y + dy, tile)
        }
      }
    })
  }

  function addDeleteMenu(menus: Map<string, () => void>) {
    const basicGroup = ctx.groups.tiles
    const select = ctx.select

    if (!select) return

    const { start, end } = select

    menus.set('delete', () => {
      const sx = Math.min(start.x, end.x)
      const ex = Math.max(start.x, end.x)
      const sy = Math.min(start.y, end.y)
      const ey = Math.max(start.y, end.y)

      for (let x = sx; x <= ex; x++) {
        for (let y = sy; y <= ey; y++) {
          basicGroup.removeAt(x, y)
        }
      }
    })
  }

  function addExportMenu(menus: Map<string, () => void>) {
    const selected = ctx.select && ctx.select.start && ctx.select.end
    if (!selected) return

    menus.set('export selected area', () => {
      const { start, end } = ctx.select!
      const startPos = indexToPosition(start)
      const endPos = indexToPosition({ x: end.x + 1, y: end.y + 1 })
      const width = endPos.x - startPos.x
      const height = endPos.y - startPos.y
      const stage = ctx.stage
      const crop = {
        x: startPos.x + stage.x(),
        y: startPos.y + stage.y(),
        width: width,
        height: height
      }
      const tiles = ctx.groups.tiles
      const dataURL = tiles.toDataURL(crop)
      const link = document.createElement('a')
      link.href = dataURL
      link.download = 'selected_area.png'
      link.click()
    })
  }

  function enableMoveOrSelect() {
    enableMouseDrag()
    enableSelect()
  }

  function enableMouseDrag() {
    const stage = ctx.stage
    // 鼠标拖动相关变量
    let isDragging = false
    let lastPos: Vector2d | null = null

    // 监听鼠标按下事件
    stage.on('mousedown touchstart', (e) => {
      if (e.evt.altKey) { // 检查是否按下 Alt 键
        isDragging = true
        lastPos = stage.getPointerPosition()
      }
    })

    // 监听鼠标移动事件
    stage.on('mousemove touchmove', (e) => {
      if (!isDragging) return
      if (!e.evt.altKey) return // 检查是否持续按下 Alt 键
      const newPos = stage.getPointerPosition()
      if (newPos === null || lastPos === null) return
      const dx = newPos.x - lastPos.x
      const dy = newPos.y - lastPos.y

      // 移动舞台
      stage.position({
        x: stage.x() + dx,
        y: stage.y() + dy
      })

      lastPos = newPos
    })

    // 监听鼠标释放事件
    stage.on('mouseup touchend mouseleave', (e) => {
      if (!isDragging) return
      if (!e.evt.altKey) return // 检查是否在释放时按下 Alt 键
      isDragging = false
      enableShowGrid()
    })
  }

  function enableSelect() {
    const stage = ctx.stage
    const ui = ctx.groups.ui

    const rect = new Konva.Rect({
      x: 0,
      y: 0,
      width: 0,
      height: 0,
      dash: [5, 5],
      stroke: 'white',
      strokeWidth: 2,
    })
    ui.add(rect)

    // 监听鼠标按下事件
    stage.on('mousedown', (e) => {
      if (e.evt.button !== 0) return
      if (e.evt.altKey) return // 按下 Alt 键时不进行选择操作
      const gridSize = ctx.gridsize

      const idx = getPointerGridIndex()
      ctx.select = {
        selecting: true,
        start: idx,
        end: idx
      }

      const pos = indexToPosition(idx)

      rect.setAttrs({
        x: pos.x,
        y: pos.y,
        width: gridSize,
        height: gridSize
      })
    })

    // 监听鼠标移动事件
    stage.on('mousemove', (e) => {
      if (e.evt.altKey) return // 按下 Alt 键时不进行选择操作
      const select = ctx.select
      if (!select || !select.selecting) return
      const currIdx = getPointerGridIndex()
      const { x: sx, y: sy } = select.start

      // 计算矩形的起始点和结束点
      const minX = Math.min(sx, currIdx.x)
      const maxX = Math.max(sx, currIdx.x)
      const minY = Math.min(sy, currIdx.y)
      const maxY = Math.max(sy, currIdx.y)

      const startPos = indexToPosition({ x: minX, y: minY })
      const endPos = indexToPosition({ x: maxX + 1, y: maxY + 1 })

      const width = endPos.x - startPos.x
      const height = endPos.y - startPos.y

      // 更新矩形选区的位置和大小
      rect.setAttrs({
        x: startPos.x,
        y: startPos.y,
        width: width,
        height: height
      })
    })

    // 监听鼠标松开事件
    stage.on('mouseup', (e) => {
      if (e.evt.altKey) return
      const select = ctx.select
      if (!select || !select.selecting) return
      const idx = getPointerGridIndex()
      select.end = idx
      select.selecting = false

      const image = ctx.groups.tiles.getAt(idx.x, idx.y)
      if (image) {
        ctx.eventbus.emit('tilesTileSelected', image as Konva.Group)
      }
    })
  }

  function enableParseImage() {
    const gridSize = ctx.gridsize
    const tiles = ctx.groups.tiles

    // 监听整个文档的粘贴事件
    document.addEventListener('paste', (event) => {
      // 获取剪贴板数据
      const images = event.clipboardData?.items
      if (!images) return

      // 检查是否有选中的网格
      const select = ctx.select
      if (!select || !select.start || !select.end) return

      const { start, end } = select
      const minX = Math.min(start.x, end.x)
      const maxX = Math.max(start.x, end.x)
      const minY = Math.min(start.y, end.y)
      const maxY = Math.max(start.y, end.y)

      for (let i = 0; i < images.length; i++) {
        const item = images[i]

        // 检查是否为图片数据
        if (item.type.indexOf('image') !== -1) {
          // 获取图片数据的文件
          const file = item.getAsFile()
          if (!file) return

          // 遍历选中的网格区域
          for (let x = minX; x <= maxX; x++) {
            for (let y = minY; y <= maxY; y++) {
              // 创建一个临时的 URL 来表示该图片
              const url = URL.createObjectURL(file)
              const img = new Image()
              img.src = url
              img.onload = () => {
                // 创建一个 Konva 图像对象
                const image = new Konva.Image({
                  image: img,
                  width: gridSize,
                  height: gridSize,
                })
                const grid = new Konva.Group({
                  index: { x, y },
                })
                grid.add(image)

                tiles?.removeAt(x, y)
                tiles?.addAt(x, y, grid)

                // 释放临时的 URL
                URL.revokeObjectURL(url)
              }
            }
          }
        }
      }
    })
  }

  function getPointerGridIndex(): Vector2d {
    const stage = ctx.stage
    const gridSize = ctx.gridsize

    const pos = stage.getPointerPosition()
    if (!pos) return { x: 0, y: 0 }

    const scale = stage.scaleX()
    const scaledSize = gridSize * scale
    const [sx, sy] = [stage.x(), stage.y()]

    const x = Math.floor((pos.x - sx) / scaledSize)
    const y = Math.floor((pos.y - sy) / scaledSize)

    return { x, y }
  }

  function indexToPosition(index: Vector2d): Vector2d {
    const gridSize = ctx.gridsize
    return { x: index.x * gridSize, y: index.y * gridSize }
  }
}